;;; core-env.el --- Spacemacs Core File -*- lexical-binding: t -*-
;;
;; Copyright (c) 2012-2025 Sylvain Benner & Contributors
;;
;; Author: Sylvain Benner <sylvain.benner@gmail.com>
;; URL: https://github.com/syl20bnr/spacemacs
;;
;; This file is not part of GNU Emacs.
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.


(require 'core-dotspacemacs)
(require 'load-env-vars)

(defvar spacemacs-env-vars-file
  (concat (or dotspacemacs-directory "~/") ".spacemacs.env")
  "Absolute path to the env file where environment variables are set.")

(defvar spacemacs-ignored-environment-variables
  '(
    "DBUS_SESSION_BUS_ADDRESS"
    "DESKTOP_STARTUP_ID"
    "DISPLAY"
    "GPG_AGENT_INFO"
    "ICEAUTHORITY"
    "INVOCATION_ID"
    "JOURNAL_STREAM"
    "MANAGERPID"
    "SESSION_MANAGER"
    "SSH_AGENT_PID"
    "SSH_AUTH_SOCK"
    "SYSTEMD_EXEC_PID"
    "XAUTHORITY"
    "XDG_SESSION_TYPE"
    )
  "Ignored environments variables.
Environment variables with names matching these regexps are not
imported into the `.spacemacs.env' file.")

(defvar spacemacs--spacemacs-env-loaded nil
  "non-nil if `spacemacs/load-spacemacs-env' has been called at least once.")

(defun spacemacs//init-spacemacs-env (&optional force)
  "Attempt to fetch the environment variables from the users shell.
This solution is far from perfect and we should not rely on this function
a lot. We use it only to initialize the env file when it does not exist
yet.
If FORCE is non-nil then force the initialization of the file, note that the
current contents of the file will be overwritten."
  (when (or force (not (file-exists-p spacemacs-env-vars-file)))
    (with-temp-file spacemacs-env-vars-file
      (let ((shell-command-switches (cond
                                     ((or(eq system-type 'darwin)
                                         (eq system-type 'cygwin)
                                         (eq system-type 'gnu/linux)
                                         (eq system-type 'android))
                                      ;; execute env twice, once with a
                                      ;; non-interactive login shell and
                                      ;; once with an interactive shell
                                      ;; in order to capture all the init
                                      ;; files possible.
                                      '("-lc" "-ic"))
                                     ((eq system-type 'windows-nt) '("-c"))))
            (tmpfile (make-temp-file spacemacs-env-vars-file))
            (executable (cond ((or(eq system-type 'darwin)
                                  (eq system-type 'cygwin)
                                  (eq system-type 'gnu/linux)
                                  (eq system-type 'android)) "env")
                              ((eq system-type 'windows-nt) "set"))))
        (insert
         (concat
          "# ---------------------------------------------------------------------------\n"
          "#                    Spacemacs environment variables\n"
          "# ---------------------------------------------------------------------------\n"
          "# This file has been generated by Spacemacs. It contains all environment\n"
          "# variables defined in your default shell except those with names matching\n"
          "# regexps in `spacemacs-ignored-environment-variables'. If you add any\n"
          "# duplicate settings for a variable, only the first setting is effective.\n"
          "# PATH is a special case: all PATH settings are read, each non-duplicate\n"
          "# directory entry is prepended to the `exec-path' variable, and then PATH is\n"
          "# set to the final value of `exec-path'.\n"
          "#\n"
          "# You can safely edit this file to change values or add or remove entries.\n"
          "# Spacemacs won't overwrite the file unless you call the function\n"
          "# `spacemacs/force-init-spacemacs-env'.\n"
          "#\n"
          "# If you don't want to use this file but would rather manage your environment\n"
          "# variables yourself, remove the call to `spacemacs/load-spacemacs-env' from\n"
          "# the `dotspacemacs/user-env' function in your dotfile and replace it with\n"
          "# your own initialization code. You can use `exec-path-from-shell' if you add\n"
          "# it to your additional packages, or simply use `setenv' and\n"
          "# `(add-to-list 'exec-path ...)', which are built-in.\n"
          "#\n"
          "# It is recommended to use this file as it unambiguously and explicitly sets\n"
          "# the values of your environment variables.\n"
          "# ---------------------------------------------------------------------------\n"
          "\n"
          "# Environment variables:\n"
          "# ----------------------\n"))
        (let ((process-environment initial-environment)
              (env-point (point)))
          (dolist (shell-command-switch shell-command-switches)
            (call-process-shell-command
             (concat executable " > " (shell-quote-argument tmpfile)))
            (insert-file-contents tmpfile))
          (delete-file tmpfile)
          ;; sort the environment variables
          (sort-regexp-fields nil "^.*$" ".*?=" env-point (point-max))
          ;; remove adjacent duplicated lines
          (delete-duplicate-lines env-point (point-max) nil t)
          ;; remove ignored environment variables
          (dolist (v spacemacs-ignored-environment-variables)
            (flush-lines v env-point (point-max))))))
    (spacemacs-buffer/warning
     (concat "Spacemacs has imported your environment variables from "
             "your shell and saved them to `%s'.\n"
             "Open this file for more info (SPC f e e) or call "
             "`spacemacs/edit-env' function.")
     spacemacs-env-vars-file)))

(defun spacemacs/force-init-spacemacs-env ()
  "Forces a reinitialization of environment variables."
  (interactive)
  (spacemacs//init-spacemacs-env t))

(defun spacemacs/edit-env ()
  "Open the `.spacemacs.env' file for editing."
  (interactive)
  (if (and spacemacs--spacemacs-env-loaded
           (file-exists-p spacemacs-env-vars-file))
      (progn
        (find-file spacemacs-env-vars-file)
        (when (fboundp 'dotenv-mode)
          (dotenv-mode)))
    ;; fallback to the dotspacemacs/user-env
    (dotspacemacs/go-to-user-env)))

(defun spacemacs/load-spacemacs-env (&optional force)
  "Load the environment variables from the `.spacemacs.env' file.
If FORCE is non-nil then force the loading of environment variables from env.
file."
  (interactive "P")
  (setq spacemacs--spacemacs-env-loaded t)
  (when (or force (display-graphic-p))
    (spacemacs//init-spacemacs-env force)
    (load-env-vars spacemacs-env-vars-file)))

(provide 'core-env)
